import sys

import fpga
import concurrent.futures as futures
from concurrent.futures import ThreadPoolExecutor
import logging
from logging.handlers import TimedRotatingFileHandler
import os
import utils
import subprocess
import argparse

if __name__ == '__main__':
    # 解析参数
    argparser = argparse.ArgumentParser(description='Pure FPGA Node')
    argparser.add_argument('-l', '--loglevel', type=str,
                           help='DEBUG, INFO, WARNING or ERROR, default INFO', default='INFO',
                           choices=('DEBUG', 'INFO', 'WARNING', 'ERROR'))
    args = argparser.parse_args()
    # 一些日志类工作
    if not os.path.isdir('log'):
        os.mkdir('log')
    logger = logging.getLogger("logger")

    handler1 = logging.StreamHandler(stream=sys.stdout)
    handler2 = TimedRotatingFileHandler(filename=f"log/run.log",
                                        when='D', interval=1, backupCount=15, encoding='utf8')
    # 遇到问题请全部使用DEBUG等级进行调试，通常情况下INFO等级已经够用
    logger.setLevel(logging.DEBUG)
    if args.loglevel.upper() == 'DEBUG':
        loglevel = logging.DEBUG
    elif args.loglevel.upper() == 'INFO':
        loglevel = logging.INFO
    elif args.loglevel.upper() == 'WARNING':
        loglevel = logging.WARNING
    elif args.loglevel.upper() == 'ERROR':
        loglevel = logging.ERROR
    else:
        loglevel = logging.INFO
    handler1.setLevel(loglevel)
    handler2.setLevel(loglevel)
    # 格式为时间-线程名-等级名-信息
    formatter = logging.Formatter("%(asctime)s %(threadName)s [%(levelname)s]: %(message)s")
    handler1.setFormatter(formatter)
    handler2.setFormatter(formatter)
    logger.addHandler(handler1)
    logger.addHandler(handler2)
    # 检查openocd是否可用，不可用则抛出RuntimeError
    ret = subprocess.run(f'openocd -v', encoding='ascii')
    if ret.returncode != 0:
        logger.error('openocd is unusable!')
        raise RuntimeError('openocd is unusable!')
    # 如果没有可支持设备，将抛出RuntimeError
    fpga.init()
    # 获取配置信息
    cfg = fpga.get_fpga_cfg()
    # 获取本机上的fpga信息
    fpgas = fpga.get_fpgas()
    device_nums = len(fpgas)
    # 信号量，等待子线程更新ipconfig
    sem = utils.sem
    with ThreadPoolExecutor(max_workers=device_nums, thread_name_prefix='TCP&') as pool:
        tasks = []
        for f in fpgas:
            f.init()
            tasks.append(pool.submit(f.run))
        # 同步ipconfig.json
        for i in range(0, device_nums):
            sem.acquire(blocking=True)
        utils.dump_ipconfig()
        # 主线程等待所有线程执行完毕
        # TODO 这里似乎可以写心跳检测程序，可以检测socket线程是否结束
        for future in futures.as_completed(tasks):
            exception = future.exception()
            if exception is not None:
                logger.exception(f'TCP thread exited with exception: \n{exception}')
            logger.info('TCP thread exited')